﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jay.Sc2.Bof.Lib.Tools;

namespace Jay.Sc2.Bof.Lib.Entity
{
	public abstract class Entity : IEntity
	{
		private int m_ID;
		private string m_Name;
		private int m_Time;
		private int m_Minerals;
		private int m_Gas;
		private int m_Supply;

		private HashSet<IEntity> m_Producers = new HashSet<IEntity>();
		private HashSet<IEntity> m_Requirements = new HashSet<IEntity>();
		private HashSet<IDictionary<IEntity, int>> m_Inputs = new HashSet<IDictionary<IEntity, int>>();

		public int ID { get { return m_ID; } set { m_ID = value; } }
		public string Name { get { return m_Name; } set { m_Name = value; } }
		public int Time { get { return m_Time; } set { m_Time = value; } }
		public int Minerals { get { return m_Minerals; } set { m_Minerals = value; } }
		public int Gas { get { return m_Gas; } set { m_Gas = value; } }
		public int Supply { get { return m_Supply; } set { m_Supply = value; } }

		public Cost BuildCost(bool firstBuild, EntityState state, List<IEntity> requirementsAlreadyNeeded, List<IEntity> added)
		{
			Cost buildCost = new Cost(Minerals, Gas, Time);
			if (Inputs.Count > 0)
			{
				Cost minInput = null;
				foreach (IDictionary<IEntity, int> set in Inputs)
				{
					Cost total = new Cost(0, 0, 0);
					foreach (KeyValuePair<IEntity, int> keyVal in set)
					{
						if (firstBuild == false || state.CountEntity(keyVal.Key) < keyVal.Value)
						{
							Cost temp = keyVal.Key.BuildCost(firstBuild, state, requirementsAlreadyNeeded, added);
							total.Time += temp.Time * keyVal.Value;
							int countProductionNeeded = Math.Max(0, keyVal.Value - state.CountProduction(keyVal.Key));
							total.Minerals += temp.Minerals * countProductionNeeded;
							total.Gas += temp.Gas * countProductionNeeded;
						}
					}
					if (minInput == null || total.IsInferiorOrEqualsCost(minInput))
						minInput = total;
				}
				buildCost.Gas += minInput.Gas;
				buildCost.Minerals += minInput.Minerals;
				buildCost.Time += minInput.Time;
			}
			if (firstBuild)
			{
				foreach (IEntity required in m_Requirements)
				{
					if (state.CountEntity(required) == 0 && !requirementsAlreadyNeeded.Contains(required) && !added.Contains(required))
					{
						added.Add(required);
						Cost temp = required.BuildCost(firstBuild, state, requirementsAlreadyNeeded, added);
						if (state.CountProduction(required) == 0)
						{
							buildCost.Gas += temp.Gas;
							buildCost.Minerals += temp.Minerals;
							buildCost.Time += temp.Time;
						}
						else
						{
							buildCost.Time += state.MinBuildTime(required);
						}
					}
				}
			}
			return buildCost;
		}

		public abstract string Type { get; }

		public ISet<IEntity> Producers { get { return m_Producers; } }
		public ISet<IEntity> Requirements { get { return m_Requirements; } }
		public ISet<IDictionary<IEntity, int>> Inputs { get { return m_Inputs; } }

		public override string ToString() { return Name; }
	}
}
